package org.example.spring.ioc.source.factory;


import org.example.spring.ioc.source.config.*;
import org.example.spring.ioc.source.converter.IntegerTypeConverter;
import org.example.spring.ioc.source.converter.StringTypeConverter;
import org.example.spring.ioc.source.converter.TypeConverter;
import org.example.spring.ioc.source.util.ReflectUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName DefaultListableBeanFactory
 * @Description 最终实现类
 * @Author ihcy
 * @Date 2019/7/23 14:03
 * @Version 1.0
 **/
public class DefaultListableBeanFactory extends AbstractBeanFactory {
    /**
     * bean定义信息
     */
    private Map<String, BeanDefinition> beanDefinitions = new HashMap<>();

    /**
     * bean Map
     */
    private Map<String, Object> singletonBeans = new HashMap<>();

    /**
     * 资源列表
     */
    private List<Resource> resources = new ArrayList<>();

    /**
     * 类型装换
     */
    private List<TypeConverter> converters = new ArrayList<>();

    public DefaultListableBeanFactory(String location) {
        // 注册资源类
        registerResources();

        // 读取配置文件信息
        Resource resource = getResource(location);
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader();
        xmlBeanDefinitionReader.loadBeanDefinitions(this, resource);
        // 将bean 信息封装到BeanDefinition 中

    }

    @Override
    public Object getBean(String name) {
        Object instance = singletonBeans.get(name);
        if (instance != null) {
            return instance;
        }
        // 先获取BeanDefinition
        BeanDefinition beanDefinition = this.beanDefinitions.get(name);
        String beanClassName = beanDefinition.getBeanClassName();
        // 通过反射创建实例
        instance = ReflectUtils.createObject(beanClassName);
        // 设置参数
        setProperty(instance, beanDefinition);
        // 初始化
        initBean(instance, beanDefinition);
        return instance;
    }


    @Override
    public <T> T getBean(Class<T> clazz) {
        return super.getBean(clazz);
    }

    /**
     * 处理属性信息
     *
     * @param instance
     * @param beanDefinition
     */
    private void setProperty(Object instance, BeanDefinition beanDefinition) {
        List<PropertyValue> propertyValues = beanDefinition.getPropertyValues();

        for (PropertyValue propertyValue : propertyValues) {
            Object availableValue = null;
            if (propertyValue.getValue() instanceof TypedStringValue) {
                // TypedStringValue  value 标签
                TypedStringValue typedStringValue = (TypedStringValue) propertyValue.getValue();
                for (TypeConverter converter : converters) {
                    if (converter.isType(typedStringValue.getTargetType())) {
                        availableValue = converter.convert(typedStringValue.getValue());
                    }
                }
            } else if (propertyValue.getValue() instanceof RuntimeBeanReference) {
                // ref标签
                RuntimeBeanReference runtimeBeanReference = (RuntimeBeanReference) propertyValue.getValue();
                String ref = runtimeBeanReference.getRef();
                // 递归调用getBean方法
                availableValue = getBean(ref);
            }

            // 调动反射进行赋值
            ReflectUtils.setProperty(instance, propertyValue.getName(), availableValue);
        }
    }

    /**
     * 初始化Bean 执行initMethod
     *
     * @param instance
     * @param beanDefinition
     */
    private void initBean(Object instance, BeanDefinition beanDefinition) {
        String initMethod = beanDefinition.getInitMethod();
        if (initMethod == null || "".equals(initMethod)) {
            return;
        }
        ReflectUtils.invokeMethod(instance, initMethod);
    }

    /**
     * 注册beanDefinition
     *
     * @param beanName
     * @param beanDefinition
     */
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
//        if (beanName == null || !beanDefinitions.containsKey(beanName)) {
//            throw new RuntimeException("beanName 为空或者异常");
//        }
        this.beanDefinitions.put(beanName, beanDefinition);
    }

    /**
     * 添加资源种类：本地文件，url等。示例只有一种e
     */
    private void registerResources() {
        resources.add(new ClassPathResource());
        converters.add(new StringTypeConverter());
        converters.add(new IntegerTypeConverter());
    }

    /**
     * 获取Resource
     *
     * @param location
     * @return
     */
    private Resource getResource(String location) {
        for (Resource resource : resources) {
            if (resource.isCanRead(location)) {
                return resource;
            }
        }
        throw new RuntimeException("location 文件位置可读");
    }


}
